<template>
  <view class="x-map">
    <view class="map-container" id="mapContainer" :change:props="leaflet.propsChange" :props="mapProps"></view>
    <view class="controls" v-if="showControls">
      <view class="control-btn" @click="zoomIn">
        <view class="zoom-icon plus"></view>
      </view>
      <view class="control-btn" @click="zoomOut">
        <view class="zoom-icon minus"></view>
      </view>
      <view class="control-btn" @click="moveToLocation">
        <view class="location-icon"></view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  name: 'x-map',
  props: {
    latitude: {
      type: Number,
      default: 35.86166
    },
    longitude: {
      type: Number,
      default: 104.195397
    },
    markers: {
      type: Array,
      default: () => []
    },
    showControls: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      scale: 4,
      mapProps: {
        center: [this.latitude, this.longitude],
        zoom: this.scale,
        markers: this.markers
      }
    }
  },
  watch: {
    latitude(val) {
      this.updateMapProps()
    },
    longitude(val) {
      this.updateMapProps()
    },
    markers: {
      handler(val) {
        this.updateMapProps()
      },
      deep: true
    },
    scale(val) {
      this.updateMapProps()
    }
  },
  methods: {
    updateMapProps() {
      this.mapProps = {
        center: [this.latitude, this.longitude],
        zoom: this.scale,
        markers: this.markers
      }
    },
    zoomIn() {
      if (this.scale < 20) {
        this.scale++
      }
    },
    zoomOut() {
      if (this.scale > 3) {
        this.scale--
      }
    },
    moveToLocation() {
      uni.getLocation({
        type: 'gcj02',
        success: (res) => {
          this.$emit('update:latitude', res.latitude)
          this.$emit('update:longitude', res.longitude)
        }
      })
    }
  }
}
</script>

<script module="leaflet" lang="renderjs">
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'

// 简单的QuadTree实现
function QuadTree(boundary, capacity) {
            this.boundary = boundary;
            this.capacity = capacity;
            this.points = [];
            this.divided = false;
        }

        QuadTree.prototype.subdivide = function() {
            let x = this.boundary.x;
            let y = this.boundary.y;
            let w = this.boundary.w / 2;
            let h = this.boundary.h / 2;

            let ne = new Rectangle(x + w, y - h, w, h);
            this.northeast = new QuadTree(ne, this.capacity);
            let nw = new Rectangle(x - w, y - h, w, h);
            this.northwest = new QuadTree(nw, this.capacity);
            let se = new Rectangle(x + w, y + h, w, h);
            this.southeast = new QuadTree(se, this.capacity);
            let sw = new Rectangle(x - w, y + h, w, h);
            this.southwest = new QuadTree(sw, this.capacity);

            this.divided = true;
        }

        QuadTree.prototype.insert = function(point) {
            if (!this.boundary.contains(point)) {
                return false;
            }

            if (this.points.length < this.capacity) {
                this.points.push(point);
                return true;
            } else {
                if (!this.divided) {
                    this.subdivide();
                }
                if (this.northeast.insert(point) || this.northwest.insert(point) ||
                    this.southeast.insert(point) || this.southwest.insert(point)) {
                    return true;
                }
            }
        }

        QuadTree.prototype.query = function(range, found) {
            if (!found) {
                found = [];
            }

            if (!this.boundary.intersects(range)) {
                return found;
            } else {
                for (let p of this.points) {
                    if (range.contains(p)) {
                        found.push(p);
                    }
                }
                if (this.divided) {
                    this.northwest.query(range, found);
                    this.northeast.query(range, found);
                    this.southwest.query(range, found);
                    this.southeast.query(range, found);
                }
                return found;
            }
        }

        QuadTree.prototype.closest = function(target, maxDistance, best) {
            if (!best) {
                best = { point: null, distance: maxDistance };
            }

            if (this.boundary.intersectsCircle(target, best.distance)) {
                for (let p of this.points) {
                    let d = dist(target, p);
                    if (d < best.distance) {
                        best.point = p;
                        best.distance = d;
                    }
                }

                if (this.divided) {
                    best = this.northwest.closest(target, best.distance, best);
                    best = this.northeast.closest(target, best.distance, best);
                    best = this.southwest.closest(target, best.distance, best);
                    best = this.southeast.closest(target, best.distance, best);
                }
            }

            return best;
        }

        function Rectangle(x, y, w, h) {
            this.x = x;
            this.y = y;
            this.w = w;
            this.h = h;
        }

        Rectangle.prototype.contains = function(point) {
            return (point.x >= this.x - this.w &&
                    point.x < this.x + this.w &&
                    point.y >= this.y - this.h &&
                    point.y < this.y + this.h);
        }

        Rectangle.prototype.intersects = function(range) {
            return !(range.x - range.w > this.x + this.w ||
                     range.x + range.w < this.x - this.w ||
                     range.y - range.h > this.y + this.h ||
                     range.y + range.h < this.y - this.h);
        }

        Rectangle.prototype.intersectsCircle = function(center, radius) {
            let xDist = Math.abs(center.x - this.x);
            let yDist = Math.abs(center.y - this.y);

            let r = radius;
            let w = this.w;
            let h = this.h;

            let edges = Math.pow(xDist - w, 2) + Math.pow(yDist - h, 2);

            // no intersection
            if (xDist > (r + w) || yDist > (r + h)) {
                return false;
            }

            // intersection within the circle and the rectangle
            if (xDist <= w || yDist <= h) {
                return true;
            }

            // intersection on the edge of the circle
            return edges <= Math.pow(r, 2);
        }

        function Point(x, y, data) {
            this.x = x;
            this.y = y;
            this.data = data;
        }

        function dist(a, b) {
            return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
        }

function cloneDeep(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    if (Array.isArray(obj)) {
        return obj.map(item => cloneDeep(item));
    }

    const cloned = {};
    for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            cloned[key] = cloneDeep(obj[key]);
        }
    }
    return cloned;
}

export default {
  data() {
    return {
      map: null,
      canvas: null,
      ctx: null,
      quadTree: null,
      cachedMarkers: [],
      hoveredMarkerId: null,
      normalMarkerPath: null,
      hoverMarkerPath: null
    }
  },
  mounted() {
    uni.downloadFile({
      url: 'https://cdn-icons-png.flaticon.com/512/854/854866.png',
      success: (res)=> {
        console.log(res,'iiii');
        
        const image = new Image()
          image.src = res.tempFilePath
          image.onload = () => {
            this.normalMarkerPath = image
          }
      }
    })
    uni.downloadFile({
      url: 'https://cdn-icons-png.flaticon.com/512/854/854878.png',
      success: (res)=> {
        const image = new Image()
          image.src = res.tempFilePath
          image.onload = () => {
            this.hoverMarkerPath = image
          }
      }
    })
    this.initMap()
  },
  methods: {
    initMap() {
      // 初始化地图
      this.map = L.map('mapContainer').setView([35.86166, 104.195397], 4)
      
      // 添加底图
      // https://baijiahao.baidu.com/s?id=1804182521779699533&wfr=spider&for=pc
      // 高德http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}
      // 腾讯https://rt1.map.gtimg.com/tile?z={z}&x={x}&y={y}&styleid=0&version=256
      L.tileLayer('http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}', {
        maxZoom: 18
      }).addTo(this.map)

      // 创建 Canvas 图层
      this.createCanvasLayer()
      
      // 初始化 QuadTree
      const boundary = new Rectangle(104.195397, 35.86166, 60, 30)
      this.quadTree = new QuadTree(boundary, 4)

      // 绑定事件
      this.bindEvents()
    },

    createCanvasLayer() {
      this.canvas = document.createElement('canvas')
      const size = this.map.getSize()
      this.canvas.width = size.x
      this.canvas.height = size.y
      this.canvas.className = 'leaflet-canvas-layer'
      this.ctx = this.canvas.getContext('2d')
      this.map.getPanes().overlayPane.appendChild(this.canvas)
    },

    drawMarkers() {
      if (!this.ctx || !this.canvas) return
      
      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
      const zoom = this.map.getZoom()
      const markerSize = Math.max(2, zoom - 4)
      var useImage = zoom > 8; // 设定缩放等级阈值

      console.time('drawMarkers')
      this.cachedMarkers.forEach(marker => {
        const point = this.map.latLngToContainerPoint([marker.data.latitude, marker.data.longitude])

        if (useImage) {
          // 绘制标记图片和标签
          var img = marker.data.id === this.hoveredMarkerId ? this.hoverMarkerPath : this.normalMarkerPath;
          this.ctx.drawImage(img, point.x - 12.5, point.y - 41, 25, 41); // 调整图片大小和位置
          this.ctx.font = '12px Arial';
          this.ctx.fillStyle = 'black';
          this.ctx.fillText('Marker ' + marker.data.id, point.x - 15, point.y - 45);
        } else {
          // 绘制圆形标记点
          this.ctx.beginPath();
          this.ctx.arc(point.x, point.y, markerSize, 0, 2 * Math.PI, false);
          this.ctx.fillStyle = marker.data.id === this.hoveredMarkerId ? 'blue' : 'red'; // 鼠标移入时变色
          this.ctx.fill();
          this.ctx.closePath();
        }
      })
      console.timeEnd('drawMarkers')
    },

    updateMarkers() {
      if (!this.map || !this.quadTree) return

      const bounds = this.map.getBounds()
      const sw = bounds.getSouthWest()
      const ne = bounds.getNorthEast()

      const range = new Rectangle((sw.lng + ne.lng) / 2, (sw.lat + ne.lat) / 2, (ne.lng - sw.lng) / 2, (ne.lat - sw.lat) / 2);

      this.cachedMarkers = this.quadTree.query(range)
      this.drawMarkers()
    },

    // 获取鼠标移入的标记点
    getHoveredMarker(latlng) {
      var target = { x: latlng.lng, y: latlng.lat };
      var closestPoint = this.quadTree.closest(target, Infinity).point;
      if (closestPoint) {
          var point = this.map.latLngToContainerPoint([closestPoint.y, closestPoint.x]);
          var targetPoint = this.map.latLngToContainerPoint([target.y, target.x]);
          var useImage = this.map.getZoom() > 8;
          var markerWidth = 25;
          var markerHeight = 41;
          var dx = Math.abs(point.x - targetPoint.x);
          var dy = point.y - targetPoint.y;
          
          if (useImage && dx <= markerWidth / 2 && dy > 0 && dy <= markerHeight) {
              return closestPoint.data;
          }
      }

      return null;
    },

    bindEvents() {
      this.map.on('click', (e) => {
        var latlng = this.map.latLngToContainerPoint(e.latlng);
        var hoveredMarker = this.getHoveredMarker(e.latlng);
        if (hoveredMarker) {
            this.hoveredMarkerId = hoveredMarker.id;
        } else {
            this.hoveredMarkerId = null;
        }
        this.drawMarkers();
      })

      this.map.on('move', () => {
        const size = this.map.getSize()
        this.canvas.width = size.x
        this.canvas.height = size.y
        const offset = this.map.latLngToLayerPoint(this.map.getBounds().getNorthWest())
        L.DomUtil.setPosition(this.canvas, offset)
        this.updateMarkers()
      })

      this.map.on('zoom', () => {
        const size = this.map.getSize()
        this.canvas.width = size.x
        this.canvas.height = size.y
        const offset = this.map.latLngToLayerPoint(this.map.getBounds().getNorthWest())
        L.DomUtil.setPosition(this.canvas, offset)
        this.updateMarkers()
      })

      this.map.on('resize', () => {
        const size = this.map.getSize()
        this.canvas.width = size.x
        this.canvas.height = size.y
        this.updateMarkers()
      })
    },

    propsChange(newProps) {
      if (!this.map) return
      console.log(newProps, 'newProps');
      
      const newProps1 = cloneDeep(newProps)
      
      // 更新地图视图
      this.map.setView(newProps1.center, newProps1.zoom)
      
      // 创建新的 QuadTree
      const boundary = new Rectangle(105, 35, 40, 20) // 覆盖中国大陆区域
      this.quadTree = new QuadTree(boundary, 4)
      
      // 插入所有点
      if (newProps1.markers && newProps1.markers.length > 0) {
          newProps1.markers.forEach(marker => {
              const point = new Point(marker.longitude, marker.latitude, marker)
              this.quadTree.insert(point)
          })
          
          // 立即更新显示
          this.updateMarkers()
      }
    }
  }
}
</script>

<style>
.x-map {
  position: relative;
  width: 100%;
  height: 100%;
}

.map-container {
  width: 100%;
  height: 100%;
}

.leaflet-canvas-layer {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
}

.controls {
  position: absolute;
  right: 16px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.control-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  background-color: #fff;
  border-radius: 4px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
  cursor: pointer;
}

.zoom-icon {
  position: relative;
  width: 16px;
  height: 16px;
}

.zoom-icon::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 2px;
  background-color: #606266;
  transform: translateY(-50%);
}

.zoom-icon.plus::after {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 2px;
  height: 100%;
  background-color: #606266;
  transform: translateX(-50%);
}

.location-icon {
  width: 16px;
  height: 16px;
  border: 2px solid #606266;
  border-radius: 50%;
  position: relative;
}

.location-icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 4px;
  height: 4px;
  background-color: #606266;
  border-radius: 50%;
  transform: translate(-50%, -50%);
}
</style>